package com.example.flow.controller;

import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * Author wangJinLong
 * Date 2025/11/14 16:52
 **/
@Slf4j
@RestController
@RequestMapping("/sse")
public class SseDemoController {

    // 存储所有连接的客户端 - 关键：这个列表保存了所有浏览器的连接
    private final List<SseEmitter> emitters = new CopyOnWriteArrayList<>();

    /**
     * 订阅SSE事件 - 前端通过这个接口建立连接
     * 当浏览器打开页面时，就会调用这个接口
     */
    @GetMapping(path = "/subscribe", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public SseEmitter subscribe() {
        // 创建SSE发射器，3600000毫秒=1小时超时
        SseEmitter emitter = new SseEmitter(3600_000L);
        log.info("新的SSE客户端连接成功");

        // 将新连接加入到客户端列表
        emitters.add(emitter);

        // 设置连接完成时的回调（用户关闭页面时会触发）
        emitter.onCompletion(() -> {
            log.info("SSE客户端断开连接（正常完成）");
            emitters.remove(emitter);
        });

        // 设置超时时的回调
        emitter.onTimeout(() -> {
            log.info("SSE客户端断开连接（超时）");
            emitters.remove(emitter);
        });

        // 设置错误时的回调
        emitter.onError(e -> {
            log.error("SSE客户端连接错误", e);
            emitters.remove(emitter);
        });

        // 发送初始测试消息 - 告诉前端连接成功了
        try {
            News testNews = new News();
            testNews.setTitle("连接成功");
            testNews.setContent("您已成功连接到SSE服务");
            emitter.send(SseEmitter.event()
                    .name("news")    // 事件名称，前端根据这个来区分不同消息
                    .data(testNews));  // 实际发送的数据
        } catch (IOException e) {
            log.error("发送初始消息失败", e);
        }

        return emitter;
    }

    /**
     * 发送新闻通知 - 前端调用这个接口来发布新闻
     * 这个方法会把新闻推送给所有连接的浏览器
     */
    @PostMapping("/send-news")
    public void sendNews(@RequestBody News news) {
        List<SseEmitter> deadEmitters = new ArrayList<>();

        log.info("开始向 {} 个客户端发送新闻: {}", emitters.size(), news.getTitle());

        // 向所有客户端发送消息 - 关键：遍历所有连接
        emitters.forEach(emitter -> {
            try {
                // 向每个客户端发送新闻数据
                emitter.send(SseEmitter.event()
                        .name("news")// 事件名称，前端监听这个事件
                        .data(news));// 新闻数据
                log.info("新闻发送成功到客户端");
            } catch (IOException e) {
                // 发送失败，说明这个连接可能已经断开
                log.error("发送新闻到客户端失败", e);
                deadEmitters.add(emitter);
            }
        });

        // 移除已经断开的连接，避免内存泄漏
        emitters.removeAll(deadEmitters);
        log.info("清理了 {} 个无效连接", deadEmitters.size());
    }

    // 新闻数据模型 - 简单的Java类，用来存储新闻数据
    @Getter
    public static class News {
        // getters and setters
        private String title;  // 新闻标题
        private String content; // 新闻内容

        public void setTitle(String title) {
            this.title = title;
        }

        public void setContent(String content) {
            this.content = content;
        }
    }
}
